package middle;

import javax.lang.model.type.IntersectionType;
import java.util.HashMap;
import java.util.Map;

/**
 * 8. 字符串转换整数 (atoi)
 * 实现一个 atoi 函数，使其能将字符串转换成整数。
 *
 * 首先，该函数会根据需要丢弃无用的开头空格字符，直到寻找到第一个非空格的字符为止。接下来的转化规则如下：
 *
 * 如果第一个非空字符为正或者负号时，则将该符号与之后面尽可能多的连续数字字符组合起来，形成一个有符号整数。
 * 假如第一个非空字符是数字，则直接将其与之后连续的数字字符组合起来，形成一个整数。
 * 该字符串在有效的整数部分之后也可能会存在多余的字符，那么这些字符可以被忽略，它们对函数不应该造成影响。
 * 注意：假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时，则你的函数不需要进行转换，即无法进行有效转换。
 *
 * 在任何情况下，若函数不能进行有效的转换时，请返回 0 。
 *
 * 提示：
 *
 * 本题中的空白字符只包括空格字符 ' ' 。
 * 假设我们的环境只能存储 32 位大小的有符号整数，那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围，请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。
 *  
 *
 * 示例 1:
 *
 * 输入: "42"
 * 输出: 42
 * 示例 2:
 *
 * 输入: "   -42"
 * 输出: -42
 * 解释: 第一个非空白字符为 '-', 它是一个  负号。
 *      我们尽可能将负号与后面所有连续出现的数字组合起来，最后得到 -42 。
 * 示例 3:
 *
 * 输入: "4193 with words"
 * 输出: 4193
 * 解释: 转换截止于数字 '3' ，因为它的下一个字符不为数字。
 * 示例 4:
 *
 * 输入: "words and 987"
 * 输出: 0
 * 解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
 *      因此无法执行有效的转换。
 * 示例 5:
 *
 * 输入: "-91283472332"
 * 输出: -2147483648
 * 解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
 *      因此返回 INT_MIN (−231) 。
 * */
public class MyAtoi {
    // 普通方法 时间复杂度O(n)  空间复杂度:O(1) 注意事项：整数溢出
    public int atoi(String str){
        // 首先去掉首尾的空格
        String s = str.trim();
        // 定义一个符号标志
        int flag = 1;
        int i = 0;
        int result = 0;
        char[] strChar = s.toCharArray();

        if(s.length() < 1){
            return 0;
        }

        if(strChar[i] == '+' || strChar[i] == '-'){
            if(strChar[i] == '-'){
                flag = -1;
            }
            i++;
        }
        while(i < strChar.length && isDigit(strChar[i])){
            int r = strChar[i] - '0';

            if(result > Integer.MAX_VALUE / 10 || (result == Integer.MAX_VALUE / 10 && r > 7)){
                return flag > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }
            result = result * 10 + r;
            i++;
        }

        return flag * result;

    }

    // 判断是否为整数字符
    public boolean isDigit(char c){
        if('0' <= c && c <= '9'){
            return true;
        }
        return false;
    }


    /**
     * 自动机 时间复杂度 ： O(n) 空间复杂度 O(n) 这里使用到了一个字符数组 当然也可以不使用
     * 综合的时间复杂度不如第一种方法，但是思路更奇特
     *
     *
     * 思路：
     * 字符串处理的题目往往涉及复杂的流程以及条件情况，如果直接上手写程序，
     * 一不小心就会写出极其臃肿的代码。
     *
     * 因此，为了有条理地分析每个输入字符的处理方法，我们可以使用自动机这个概念：
     *
     * 我们的程序在每个时刻有一个状态 s，每次从序列中输入一个字符 c，
     * 并根据字符 c 转移到下一个状态 s'。这样，我们只需要建立一个覆盖所有情况的从 s
     * 与 c 映射到 s' 的表格即可解决题目中的问题。
     *
     * */
    public int atoi1(String str){
        // 初始化状态
        String state = "start";
        char[] chars = str.toCharArray();
        // 符号 默认为正
        int sign = 1;
        int result = 0;
        // 创建自动机
        Map<String, String[]> table = new HashMap<String, String[]>(){{
            put("start", new String[]{"start", "signed", "intNum", "end"});
            put("signed", new String[]{"end", "end", "intNum", "end"});
            put("intNum", new String[]{"end", "end", "intNum", "end"});
            put("end", new String[]{"end", "end", "end", "end"});
        }};

        for(char c : chars){
            state = table.get(state)[getCol(c)];
            if(state == "intNum"){
                if(result > Integer.MAX_VALUE / 10 || (result == Integer.MAX_VALUE / 10 && (c-'0') > 7)){
                    return sign > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
                }
                result = result * 10 + (c - '0');
            }else if(state == "signed"){
                sign = c == '+' ? 1 : -1;
            }
        }


        return sign * result;
    }

    // 返回当前字符的状态
    public int getCol(char c){
        if(c == ' ') return 0;
        if(c == '+' || c == '-') return 1;
        if(isDigit(c)) return 2;
        return 3;
    }


    // main
    public static void main(String[] args) {
        MyAtoi atoi = new MyAtoi();
        int r = atoi.atoi1("2147483648");
        System.out.print(r);
    }
}
